Print nicer messages on 'no package named' errors
authorAlex Crichton <alex@alexcrichton.com>
Sun, 11 Jan 2015 07:03:58 +0000 (23:03 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 14 Jan 2015 18:29:36 +0000 (10:29 -0800)
The new message will print out a selection of versions that were found if any
are to be had, as well as a recommendation to run `cargo update` if it's a path
dependency.

Closes #1145

src/cargo/core/resolver/mod.rs
tests/resolve.rs
tests/test_cargo_compile.rs
tests/test_cargo_registry.rs

index 066dac76fb82cfd4d0caf2ab6c4050290165c87d..11a4d0ca41e273a31f94f6508834c34ff1818b0f 100644 (file)
@@ -326,12 +326,49 @@ fn activate_deps<'a, R: Registry>(cx: Context,
             Err(human(msg))
         }
         None => {
-            Err(human(format!("no package named `{}` found (required by `{}`)\n\
+            // Once we're all the way down here, we're definitely lost in the
+            // weeds! We didn't actually use any candidates above, so we need to
+            // give an error message that nothing was found.
+            //
+            // Note that we re-query the registry with a new dependency that
+            // allows any version so we can give some nicer error reporting
+            // which indicates a few versions that were actually found.
+            let msg = format!("no matching package named `{}` found \
+                               (required by `{}`)\n\
                                location searched: {}\n\
                                version required: {}",
                               dep.get_name(), parent.get_name(),
                               dep.get_source_id(),
-                              dep.get_version_req())))
+                              dep.get_version_req());
+            let mut msg = msg;
+            let all_req = semver::VersionReq::parse("*").unwrap();
+            let new_dep = dep.clone().version_req(all_req);
+            let mut candidates = try!(registry.query(&new_dep));
+            candidates.sort_by(|a, b| {
+                b.get_version().cmp(a.get_version())
+            });
+            if candidates.len() > 0 {
+                msg.push_str("\nversions found: ");
+                for (i, c) in candidates.iter().take(3).enumerate() {
+                    if i != 0 { msg.push_str(", "); }
+                    msg.push_str(c.get_version().to_string().as_slice());
+                }
+                if candidates.len() > 3 {
+                    msg.push_str(", ...");
+                }
+            }
+
+            // If we have a path dependency with a locked version, then this may
+            // indicate that we updated a sub-package and forgot to run `cargo
+            // update`. In this case try to print a helpful error!
+            if dep.get_source_id().is_path() &&
+               dep.get_version_req().to_string().starts_with("=") &&
+               candidates.len() > 0 {
+                msg.push_str("\nconsider running `cargo update` to update \
+                              a path dependency's locked version");
+
+            }
+            Err(human(msg))
         }
     })
 }
index 92d24ed6a16bb1b7c1aed6b08924e6922d159564..9dcd97219acb17875f466e10a9606586665843d2 100644 (file)
@@ -335,7 +335,7 @@ fn resolving_but_no_exists() {
     assert!(res.is_err());
 
     assert_eq!(res.unwrap_err().to_string(), "\
-no package named `foo` found (required by `root`)
+no matching package named `foo` found (required by `root`)
 location searched: registry http://example.com/
 version required: ^1\
 ");
index 4dbea701734dbaa37400b1724dd3e8f26c43d7cb..fb81b74a8df4d038ef5b570052b5d8d0ac4402d8 100644 (file)
@@ -513,12 +513,51 @@ test!(cargo_compile_with_dep_name_mismatch {
 
     assert_that(p.cargo_process("build"),
                 execs().with_status(101).with_stderr(format!(
-r#"no package named `notquitebar` found (required by `foo`)
+r#"no matching package named `notquitebar` found (required by `foo`)
 location searched: {proj_dir}
 version required: *
 "#, proj_dir = p.url())));
 });
 
+test!(compile_path_dep_then_change_version {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [dependencies.bar]
+            path = "bar"
+        "#)
+        .file("src/lib.rs", "")
+        .file("bar/Cargo.toml", r#"
+            [package]
+            name = "bar"
+            version = "0.0.1"
+            authors = []
+        "#)
+        .file("bar/src/lib.rs", "");
+
+    assert_that(p.cargo_process("build"), execs().with_status(0));
+
+    File::create(&p.root().join("bar/Cargo.toml")).unwrap().write_str(r#"
+        [package]
+        name = "bar"
+        version = "0.0.2"
+        authors = []
+    "#).unwrap();
+
+    assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
+                execs().with_status(101).with_stderr("\
+no matching package named `bar` found (required by `foo`)
+location searched: [..]
+version required: = 0.0.1
+versions found: 0.0.2
+consider running `cargo update` to update a path dependency's locked version
+"));
+});
+
 // test!(compiling_project_with_invalid_manifest)
 
 test!(crate_version_env_vars {
index 770371d2e28f4acc9c311eb39f0c502021ce1edc..2873d2ee3af5af36a98dd1ac3762d34ec942401b 100644 (file)
@@ -100,12 +100,48 @@ test!(nonexistent {
 
     assert_that(p.cargo_process("build"),
                 execs().with_status(101).with_stderr("\
-no package named `nonexistent` found (required by `foo`)
+no matching package named `nonexistent` found (required by `foo`)
 location searched: registry file://[..]
 version required: >= 0.0.0
 "));
 });
 
+test!(wrong_version {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.0.1"
+            authors = []
+
+            [dependencies]
+            foo = ">= 1.0.0"
+        "#)
+        .file("src/main.rs", "fn main() {}");
+
+    r::mock_pkg("foo", "0.0.1", &[]);
+    r::mock_pkg("foo", "0.0.2", &[]);
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(101).with_stderr("\
+no matching package named `foo` found (required by `foo`)
+location searched: registry file://[..]
+version required: >= 1.0.0
+versions found: 0.0.2, 0.0.1
+"));
+
+    r::mock_pkg("foo", "0.0.3", &[]);
+    r::mock_pkg("foo", "0.0.4", &[]);
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(101).with_stderr("\
+no matching package named `foo` found (required by `foo`)
+location searched: registry file://[..]
+version required: >= 1.0.0
+versions found: 0.0.4, 0.0.3, 0.0.2, ...
+"));
+});
+
 test!(bad_cksum {
     let p = project("foo")
         .file("Cargo.toml", r#"
@@ -149,7 +185,7 @@ test!(update_registry {
 
     assert_that(p.cargo_process("build"),
                 execs().with_status(101).with_stderr("\
-no package named `notyet` found (required by `foo`)
+no matching package named `notyet` found (required by `foo`)
 location searched: registry file://[..]
 version required: >= 0.0.0
 "));
@@ -200,7 +236,7 @@ test!(package_with_path_deps {
 failed to verify package tarball
 
 Caused by:
-  no package named `notyet` found (required by `foo`)
+  no matching package named `notyet` found (required by `foo`)
 location searched: registry file://[..]
 version required: ^0.0.1
 "));
@@ -344,9 +380,10 @@ test!(relying_on_a_yank_is_bad {
 
     assert_that(p.process(cargo_dir().join("cargo")).arg("build"),
                 execs().with_status(101).with_stderr("\
-no package named `baz` found (required by `bar`)
+no matching package named `baz` found (required by `bar`)
 location searched: registry file://[..]
 version required: = 0.0.2
+versions found: 0.0.1
 "));
 });
 
@@ -378,7 +415,7 @@ test!(yanks_in_lockfiles_are_ok {
 
     assert_that(p.process(cargo_dir().join("cargo")).arg("update"),
                 execs().with_status(101).with_stderr("\
-no package named `bar` found (required by `foo`)
+no matching package named `bar` found (required by `foo`)
 location searched: registry file://[..]
 version required: *
 "));